/*
 *  ClientServer.h
 *  initiativeTracker
 *
 *  Created by Sam Jaffe on 5/10/12.
 *  Copyright 2012 WPI. All rights reserved.
 *
 */

#pragma once

template<class D>
class Server;
template<class D>
class Client_Thread;
template <class D>
class Server_XIO;
class Client_XIO;

class Client;
class Server_Access;

#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include "Character.h"
#include "InitTracker.h"
#include "XIOController.h"
#include "xmlHandler.h"

namespace {
    const std::string schema("initiative.xsd");
    const unsigned short defaultPort = 9371;
}

class Access {
protected:
    int access_socket;
    bool active = false;
public:
    Access(const int socket) : access_socket(socket) {}
    bool sendMessage(const std::string message);
    std::string extract(int fd, std::string terminator);
};

template<class D>
class Server {
private:
    
    int server_socket;
    bool state = false;
    const unsigned short serverPort;
    
    std::unordered_map<std::string, std::shared_ptr<Client_Thread<D>>> clients;
    std::unordered_map<std::string, std::shared_ptr<InitTracker>> events;
    std::unordered_map<std::string, std::vector<std::string> >members;
    
public:
    Server_XIO<D> request_handler;
    
    Server();
    Server(const unsigned short port);
    Server(const std::string schema);
    Server(const unsigned short port, const std::string schema);
    ~Server() {}
    
    void initiateSocket();
    void listenforClient();
    void shutdown();
    
    void register_client(const std::string idn, 
                         const Client_Thread<D>* client);
    void unreg_client(const std::string idn);
    void unreg_tracker(const std::string idn);
    void unreg_member(const std::string idn);
    void unreg_event(const std::string idn);
    
    std::shared_ptr<InitTracker> tracker(const std::string idn) const;
    std::shared_ptr<Client_Thread<D>> client(const std::string idn) const;
    std::vector<std::string> member_ids(const std::string idn) const;
    std::vector<std::shared_ptr<Client_Thread<D>>> map_members(std::vector<std::string>& ids) const;
    std::vector<std::shared_ptr<Client_Thread<D>>> member_list(const std::string idn) const;
};

template<class D>
class Client_Thread : public Access {
private:
    Server<D> *const host;
    const std::string idn;
    D data;
    const std::string disconnect;
public:
    Client_Thread(Server<D>* srv, int socket) : Access(socket) {
        host = srv;
    }
    ~Client_Thread(){}
    
    void run();
    void close();
    void getId();
    
    D setData(D newData) {
        D old = data;
        data = newData;
        return old;
    }
    
    D getData() {return data;}
};

class Client {
private:
    const Server_Access* access;
    std::shared_ptr<InitTracker> init;
public:
    Client();
    ~Client() {}
    bool setAccess(Server_Access* sac);
    std::shared_ptr<InitTracker> event() {return init;}
};

class Server_Access : public Access {
private:
    const std::string host;
    Client *const client;
    Client_XIO *const response_handler;
    const int port;
public:
    Server_Access(Client* cl, const std::string host);
    Server_Access(Client* cl, const std::string host, std::string schema);
    Server_Access(Client* cl, const std::string host, const unsigned short port);
    Server_Access(Client* cl, const std::string host, const unsigned short port, std::string schema);
    
    ~Server_Access();
    
    bool connect();
    void disconnect();
    void run();
};
